(ns alpacas.core
; (:gen-class)
  (:use seesaw.core seesaw.border)
  (:import (java.awt Component Color Container Frame)
           (java.io PushbackReader InputStreamReader FileReader StringReader InputStreamReader)
           (java.awt.event KeyEvent)
           (javax.swing JPanel JRootPane)))

(def selected (ref nil))

(defn element-to-string [e]
  (cond
    (nil? e) "nil"
    (string? e) (str "\"" e "\"")
    :else (str e)))

;todo make it 
(defn make-form [l]
  "takes a form and returns a map of forms to nested JPanels"
  ; {l 
  (doto
    (let [contains-list (some list? (rest l))]
      (apply
        (if contains-list
          vertical-panel
          horizontal-panel)
        [
        :border (compound-border
                  (line-border) ; :top 0 :left 1 :bottom 1 :right 1)
                  (empty-border :top 5 :left 30 :bottom 5 :right 5))
        :items (cons
                 (doto
                   (label
                     :border (compound-border
                               2
                               (line-border :bottom (if contains-list 1 0) :right 1)
                               (empty-border :bottom 0 :right 3))
                     :text (element-to-string (first l)))
                   (.setAlignmentY Component/TOP_ALIGNMENT))
                 (map
                   #(cond
                     (list? %)
                       (make-form %)
                     (vector? %)
                       (make-form (cons (symbol "[") %))
                     (map? %)
                       (make-form (cons (symbol "{") %))
                     (set? %)
                       (make-form (cons (symbol "#{") %))
                     :else
                       (doto
                         (label
                           :border (empty-border :top 2 :right 3 :left (if contains-list 30 0))
                           :text (element-to-string %))
                         (.setAlignmentY Component/TOP_ALIGNMENT)))
                   (rest l)))
        ]))
    (.setAlignmentY Component/TOP_ALIGNMENT)
    (.setAlignmentX Component/LEFT_ALIGNMENT)))

(defn last-child [cpn]
  (let [child-count (.getComponentCount cpn)]
    (if (< child-count 1)
      nil
      (.getComponent cpn (dec child-count)))))

(defn last-rec-child [cpn]
  (if (instance? JPanel cpn)
    (last-rec-child (last-child cpn))
    cpn))

;todo .getComponents need treelock?
(defn rel-sibling [cpn n]
  (let [parent (.getParent cpn)]
    (if (instance? JRootPane parent)
      nil
      (let [siblings (.getComponents parent)
            idx (.indexOf (map str siblings) (str cpn))]
        (if (and (>= idx (- n)) (< idx (- (count siblings) n)))
          (let [next (get siblings (+ n idx))
                child-count (.getComponentCount next)]
            (if (instance? JPanel next)
              (if (> child-count n)
                (if (pos? n)
                  (.getComponent next (dec n))
                  (.getComponent next (+ child-count n)))
                nil)
              next))
          (last-rec-child (rel-sibling parent n))))))) ;todo works only for -1

(defn select-rel [m]
  (when-not (nil? @selected)
    (let [n (rel-sibling @selected m)]
      (when-not (nil? n)
        (.setBackground @selected Color/WHITE)
        (dosync (ref-set selected n))
        (.setOpaque @selected true)
        (.setBackground @selected Color/YELLOW)))))

(defn key-pressed [e]
  ;todo assign when the containers are created  
  (let [key-code (.getKeyCode e)]
    (cond
      (= key-code KeyEvent/VK_Q) (.. e (getSource) (dispose))
      (= key-code KeyEvent/VK_LEFT) (select-rel -1)
      (= key-code KeyEvent/VK_RIGHT) (select-rel 1)
      (= key-code KeyEvent/VK_UP) (println "up")
      (= key-code KeyEvent/VK_DOWN) (println "down")
      (= key-code KeyEvent/VK_PAGE_UP)
        (let [viewport (.. e (getSource) (getRootPane) (getContentPane) (getViewport))
              view-rect (.getViewRect viewport)]
              (.translate view-rect 0 (- (.height view-rect)))
              (scroll! (.getView viewport) :to view-rect))
      (= key-code KeyEvent/VK_PAGE_DOWN)
        (let [viewport (.. e (getSource) (getRootPane) (getContentPane) (getViewport))
              view-rect (.getViewRect viewport)]
              (.translate view-rect 0 (.height view-rect))
              (scroll! (.getView viewport) :to view-rect)))))

(defn sel [cpn]
  (dosync
    (ref-set selected cpn))
  (.setBackground cpn Color/YELLOW)
  (.setOpaque @selected true))

(defn make-frame
  "makes a frame representing a list of forms"
  [forms]
  (let [panels (map make-form forms)
        first-panel (first panels)]
    (sel
      (if (< (.getComponentCount first-panel) 1)
        nil
        (.getComponent first-panel 0)))
    (frame
      :title "alpacas"
      :content (scrollable (vertical-panel :items panels))
      :on-close :dispose)))

(defn make-forms-to-panels [forms]
  (merge
    (map make-form forms)))

(defn read-forms
  "returns the forms contained in a Reader"
  [reader]
  (let [eof (Object.)
        pbr (PushbackReader. reader)]
    (loop [forms []]
      (let [form (read pbr false eof)]
        (if (= form eof)
          (list* forms)
          (recur (conj forms form)))))))

; form-to-panel

(defn -main
  [& args]
  (invoke-later
    (->
      (make-frame (read-forms (InputStreamReader. (.openStream (clojure.java.io/resource "alpacas/core.clj")))))
      pack!
      show!
      (listen :key-pressed key-pressed))))

